import numpy as np
from model.utils import get_k
import matplotlib.pyplot as plt

#实现eigenface方法
class EigenFace:
    def __init__(self):
        self.eigenfaces = [] #特征脸
        self.mean_face = None #平均脸

    def fit(self, train_data):
        num_class, M, N = train_data.shape #类别数、每一类的训练样本数、图片向量维度
        self.mean_face = np.mean(train_data, 1, dtype=np.float32) #每一类平均脸
        centered_face = (train_data - self.mean_face.repeat([M for _ in range(num_class)], axis=0).reshape(num_class, M, N)).transpose((0, 2, 1)) #中心化
        for i, face in enumerate(centered_face):
            C = np.matmul(face.T, face, dtype=np.float32)  # 求协方差阵
            eigen_values, eigen_vectors = np.linalg.eig(C) #奇异值分解
            index = np.argsort(eigen_values)[::-1] #对特征值由大到小排序
            eigen_values = np.sort(eigen_values)[::-1]
            k = get_k(eigen_values, 0.90) #取前k个特征值对应的特征向量
            selected_index = index[:k]
            selected_eigen_vectors = np.real(eigen_vectors[selected_index].T)
            eigenface = np.matmul(face, selected_eigen_vectors, dtype=np.float32)
            eigenface = eigenface.T / np.linalg.norm(eigenface.T, axis=1, keepdims=True)
            self.eigenfaces.append(eigenface.T) #计算特征脸

    #使用重构误差最小分类
    def evaluate(self, test_data, test_lable):
        correct = 0
        for index, data in enumerate(test_data):
            error = []
            for i, face in enumerate(self.eigenfaces):
                img = data - self.mean_face[i]
                pattern_vector = np.matmul(img, face)
                reconstruct = np.sum(face.T * np.expand_dims(pattern_vector, -1), 0) + self.mean_face[i]
                error.append(np.linalg.norm(data-reconstruct))
            lable = np.argmin(error)

            if lable == test_lable[index]:
                correct += 1
            # else: print('predict lable: %d  ture lable: %d' %(lable, test_lable[index]))
        return correct / test_lable.shape[0]